﻿using SuperJoe.Framework.UnitOfWorkSql;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data;
using MySql.Data.MySqlClient;
using System.Data.SqlClient;

namespace WWX.Admin.DAL
{
    public class UnitOfWork : IUnitOfWork
    {
        List<SqlEntryCommand> sqlEntryCommands = new List<SqlEntryCommand>();
        List<DataTableCommand> datatableCommands = new List<DataTableCommand>();

        private bool isInTransaction = false;

        public void Begin()
        {
            isInTransaction = true;
        }

        public void End()
        {
            MySqlTransaction tran = null;

            try
            {
                if (isInTransaction && (this.sqlEntryCommands.Count != 0 || this.datatableCommands.Count != 0))
                {
                    var sqlObjects = this.GenerateSqlObjects();
                    var datatables = this.datatableCommands.Select(n => n.Table).ToArray();

                    using (var conn = DbHelperMySQL.GetSqlConnection())
                    {
                        conn.Open();
                        tran = conn.BeginTransaction();

                        try
                        {
                            if (sqlObjects.Count != 0)
                            {
                                DbHelperMySQL.ExecuteExternalSqlTran(conn, tran, sqlObjects);
                            }

                            if (datatables.Length != 0)
                            {
                                DbHelperMySQL.BulkInsertInExternalTran(conn, tran, datatables);
                            }

                            tran.Commit();
                        }
                        catch
                        {
                            tran.Rollback();
                            throw;
                        }
                    }
                }
            }
            catch (SqlException ex)
            {
                var commands = new List<UOWCommand>();
                commands.AddRange(sqlEntryCommands);
                commands.AddRange(datatableCommands);
                throw new UnitOfWorkException(ex.Message, ex) { UOWCommand = commands };
            }
            finally
            {
                this.isInTransaction = false;
                if (this.sqlEntryCommands != null)
                {
                    this.sqlEntryCommands.Clear();
                    this.datatableCommands.Clear();
                }
            }
        }

        public int ExecuteCommand(IEnumerable<UOWCommand> commands)
        {
            var effectedRows = 0;

            var sqlEntryCommands = new List<SqlEntryCommand>();
            var datatableCommands = new List<DataTableCommand>();
            foreach (var c in commands)
            {
                if (c is SqlEntryCommand)
                {
                    sqlEntryCommands.Add(c as SqlEntryCommand);
                }
                else
                {
                    datatableCommands.Add(c as DataTableCommand);
                }
            }

            if (sqlEntryCommands.Count != 0)
            {
                effectedRows += ExecuteCommandInternal(sqlEntryCommands);
            }

            if (datatableCommands.Count != 0)
            {
                effectedRows += ExecuteCommandInternal(datatableCommands);
            }

            if (isInTransaction)//在事务处理下，统一改成1. 因为具体影响行数只能等到整个事务执行完了才能计算出来。
            {
                effectedRows = 1;
            }

            return effectedRows;
        }

        private int ExecuteCommandInternal(IEnumerable<SqlEntryCommand> sqlEntryCommands)
        {
            if (isInTransaction)
            {
                this.sqlEntryCommands.AddRange(sqlEntryCommands);
                return 1;
            }
            else
            {
                var sqlObjects = sqlEntryCommands.Select(n => new Tuple<string, MySqlParameter[]>(n.Sql, n.Parameters.Select(p => new MySqlParameter("?" + p.Key, p.Value == null ? DBNull.Value : p.Value)).ToArray())).ToList();

                if (sqlObjects.Any(n => n.Item2.Length != 0))
                {
                    return DbHelperMySQL.ExecuteSqls(sqlObjects);
                }
                else
                {
                    return DbHelperMySQL.ExecuteSqls(sqlObjects.Select(n => n.Item1));
                }
            }
        }

        private int ExecuteCommandInternal(IEnumerable<DataTableCommand> datatableCommands)
        {
            if (isInTransaction)
            {
                this.datatableCommands.AddRange(datatableCommands);
                return 1;
            }
            else
            {
                var effectRows = 0;
                var tables = datatableCommands.Select(n => n.Table);
                DbHelperMySQL.BulkInsertInInternalTran(tables.ToArray());
                effectRows = tables.Sum(t => t.Rows.Count);

                return effectRows;
            }
        }


        public void Rollback()
        {
            this.isInTransaction = false;
        }

        private IList<Tuple<string, MySqlParameter[]>> GenerateSqlObjects()
        {
            List<Tuple<string, MySqlParameter[]>> sqlObjects = new List<Tuple<string, MySqlParameter[]>>();

            foreach (var entity in sqlEntryCommands)
            {
                var parameters = entity.Parameters.Select(n => new MySqlParameter("?" + n.Key, (n.Value == null ? DBNull.Value : n.Value))).ToArray();
                sqlObjects.Add(new Tuple<string, MySqlParameter[]>(entity.Sql, parameters));
            }

            return sqlObjects;
        }

        public object ExecuteScalar(string sql, IDictionary<string, object> parameters = null)
        {
            return DbHelperMySQL.ExecuteScalar(sql, ConvertToSqlParameters(parameters));
        }

        public DataSet ExecuteDataSet(string sql, IDictionary<string, object> parameters = null)
        {
            return DbHelperMySQL.Query(sql, ConvertToSqlParameters(parameters));
        }

        public MySqlParameter[] ConvertToSqlParameters(IDictionary<string, object> parameters)
        {
            return parameters == null ? null : parameters.Select(n => new MySqlParameter("?" + n.Key, n.Value)).ToArray();
        }
    }
}
